﻿using System;
using System.IO;

namespace System.IO
{
    public class Base64Encoder : Stream
    {
        readonly TextWriter writer;
        byte[] buf = new byte[20 * 57]; // each 57 input bytes will be transformed into --> 76 output chars + CRLF
        int pos;
        long length;
        bool flashed;

        public Base64Encoder(TextWriter writer)
        {
            this.writer = writer;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (flashed)
                throw new InvalidOperationException("Once flushed, the stream can't be written anymore and can only be closed.");

            for (int i = 0; i < count; i++)
                write(buffer[offset + i]);
        }

        void write(byte b)
        {
            if (pos == buf.Length)
                flush();
            buf[pos++] = b;
        }

        void flush()
        {
            if (pos == 0)
                return;

            string x = Convert.ToBase64String(buf, 0, pos, Base64FormattingOptions.InsertLineBreaks);
            writer.Write(x);
            if (pos == buf.Length)
                writer.Write("\r\n");

            length += x.Length;
            pos = 0;
        }

        public override void Flush()
        {
            if (flashed)
                return;

            try
            {
                flush();
                writer.Flush();
            }
            finally
            {
                flashed = true;
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                flush();
                writer.Close();
            }
            base.Dispose(disposing);
        }

        #region other trivial abstract methods

        public override bool CanRead
        {
            get { return false; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Length
        {
            get { return length; }
        }

        public override long Position
        {
            get
            {
                return length;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }
        #endregion

    }
}
